home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Your Choice 3
/
Your Choice Software Collection 3.iso
/
prgmming
/
qb_tips
/
qbtips_n.doc
< prev
next >
Wrap
Text File
|
1993-07-26
|
160KB
|
4,569 lines
Name: QBTips_N.Doc Date: 8/93
Also See: QBTips_A through QBTips_M
Purpose: To provide insights and source code to help BASIC
programmers -- beginner through advanced.
Load this into your word processor or editor. Then
scan it for tidbits you think will be useful. Just
"cut & paste" sections you like to separate files,
then run the code.
Source: Below you'll find messages captured from the FidoNet
Quik_Bas echo. We captured CODE and significant tips,
and eliminated chatter.
Format: Varies, depending on the author, their programming
style, and the question or topic.
A form-feed (Chr$(12)) appears after most messages.
This allows you to print this, and have each message
(ie., each topic) start on a new page.
Recommendation: None!
Some of what you'll see below is brilliant. Some
demonstrates very poor programming techniques. But
all of it can prove useful if you have a need.
NOTE 1:
We have NOT tried all the code you see here, and some
of it may not run as-is. You may have to do a little
editing to coax it. One reason that code may not run
is that messages sometimes get truncated or mangled in
transmission. Another reason is that authors make
mistakes (or typos). Again, we haven't tried running
everything; but when you do, you'll probably quickly
spot places that need editing.
NOTE 2:
There may be near-duplicate messages. The original
author may have refined the code, or may have found
errors in the original. If you see something that
looks interesting, before you rely on the code, scan
for the topic or author to see if a new set of code is
below you -- more recent messages appear below. And
note that the next message may be in a later package.
NOTE 3:
BEFORE running any code segment, scan through it and
LOOK FOR code fragments which could be DISASTROUS!
*** We often run un-tested code fragments from a ***
*** RAM or floppy disk. And BEFORE running it we ***
*** scan for "c:" or "d:" (or other hard drive) ***
*** letters. And we also scan for .. (see below) ***
For example, scan for "OUT " -- and if you find any
verify that the code is OUTting the correct values
to the correct ports. Typos, transmission errors
or programmer mistakes could send the wrong values
to the wrong ports. At best, nothing will happen.
At worst, you might fry your monitor -- or worse.
Also look for INTERRUPT (or INTERRUPTx). These functions
are v-e-r-y useful for invoking low-level DOS or BIOS
functions. But that low-level access also comes with
some risks! Programmer or transmission errors, open
drive doors, etc., can, at best, cause your PC to hang.
At worst, you could corrupt the FAT of your hard disk.
=========================================================================
' From: VICTOR YIU Sent: 07-12-93 13:32
' To: ALL Rcvd: -NO-
' Re: BROWSER 1.0 NOTES
'
'Hi, All!
'
'Following two messages, I will present you Browser, a really quick hex
'file viewer, just like Calvin's Hexview, except using my SuperHex asm.
'procedure. Capture the basic file (BROWSER.BAS), the PostIt! file to
'make the OBJ file, and then the two messages of the asm I wrote.
'
'QB users must change all occurences of SSEG to VARSEG.
'
'Enjoy! And I want to know if it feels SNAPPY on your machine!
' ==================== Browser 1.0 =======================
' by Victor Yiu, July 1993. Released into Public Domain.
'
' *** LOAD WITH /AH SWITCH ***
' This program is a binary file viewer, in hex and ascii,
' made to look like Calvin French's HEXVIEW, PCTools' View,
' Norton's DiskEdit, and numerous others. It uses my public-
' domain SuperHex library, written in optimized assembly -- so
' it is FAST!
' (to make EXE:)
' BC BROWSER /O;
' LINK /FARC /PACKC:64000 BROWSER+C:\QB\NOCOM,,,C:\QB\BCOM45+SUPERHEX;
' ^ (include "/EX" if you won't use LZEXE or PKLITE)
'
' Compared to Clavin's HEXVIEW:
' o Mine can open files up to 128k
' o Mine is 5 million times faster (faster than PCTools or Norton)
' o Has a built-in text filter
' o Acceptable on an 4.77 MHz XT!
' o Compiles to only 25K with LZEXE or PKLITE!
'
' Next version will include search and editing features.
'
' Speed comparison (on a 10MHz XT)*:
' Calvin French's HEXVIEW: .55 K/sec
' PCTool's 4.x Edit: 3 K/sec
' Victor's SuperHEX (!): 20 K/sec ---> WOW!
'
' * I didn't do timing on my 486 because it was too darn fast!
'
' By the way -- try loading a file from the floppy -- it's fun!
' ============================================================
DEFINT A-Z ' $DYNAMIC
CONST Block = 8192, PageSize = 16 * 21, LastDataLine = 23
CONST DataFG = 7, DataBG = 1, Attrib = DataFG + DataBG * 16
CONST False = 0, True = NOT False
DECLARE SUB AdjustLastBytes (Row%)
DECLARE SUB CheckPointer (Num&)
DECLARE SUB DrawInterface ()
DECLARE SUB DrawScreen ()
DECLARE SUB LoadFile (LOFile&)
DECLARE SUB PrintHex (Num&)
DECLARE SUB ShowHex (Posit&)
DECLARE FUNCTION GetAdapterSeg% ()
DECLARE FUNCTION Signed% (Num&)
DECLARE SUB Scroll (BYVAL GoUp, BYVAL Attrib)
DECLARE SUB MemCopy (BYVAL SegFrom, BYVAL OffFrom, BYVAL SegTo,_
BYVAL OffTo, BYVAL Leng)
DECLARE SUB SuperHex (BYVAL VidSeg, BYVAL Row, BYVAL OffsetHex,_
BYVAL OffsetASCii, BYVAL segment, BYVAL Offset, BYVAL_
BackColor, BYVAL FilterOn)
' VidSeg = Video segment of adapter (B800 for color, B000 for mono)
' Row = Row to display data (1-25)
' OffsetHex = Column to display the hex digits (1-)
' OffsetASCii= Column to display the characters themselves (1-)
' Segment = Segment of source data
' Offset = Offset of source data
' BackColor = Background color in a packed byte:
' (BackGround * 16) + ForeGround
IF LEN(COMMAND$) = 0 THEN
PRINT "Syntax:"
PRINT " BROWSER <filename>"
END
END IF
DIM SHARED VidSegment, Pointer&, LastBox, LOFile&, FilterOn
OPEN COMMAND$ FOR BINARY AS #1
LOFile& = LOF(1)
IF (LOFile& + 3000 > FRE(-1)) OR (LOFile& > 131000) THEN PRINT "File too big.": END
IF LOFile& = 0 THEN CLOSE : PRINT COMMAND$; " does not exist.":KILL COMMAND$: END
DIM SHARED Array&(LOFile& \ 4& + 4)
Pointer& = 0: LastBox = 4: Null$ = CHR$(0)
CLS
VidSegment = GetAdapterSeg
DrawInterface
LOCATE 12, 33: COLOR 31, 3
PRINT " Loading file ... "; : COLOR 7
LoadFile LOFile&
IF LOFile& - 16 < PageSize THEN
LOCATE 12, 33: COLOR , 1
PRINT SPACE$(40);
END IF
DrawScreen
DO
DO: I$ = INKEY$
LOOP UNTIL LEN(I$)
IF LEN(I$) = 1 THEN I$ = UCASE$(I$)
SELECT CASE I$
CASE Null$ + "Q", CHR$(13), " " ' PageDown
Pointer& = Pointer& + PageSize
CheckPointer Pointer&
CASE Null$ + "I" ' PageUp
Pointer& = Pointer& - PageSize
CheckPointer Pointer&
CASE Null$ + "P" ' Down
IF Pointer& + PageSize < LOFile& THEN
Scroll 0, LastDataLine
Pointer& = Pointer& + 16
ShowHex Pointer&
Temp& = Pointer& + PageSize - 16
LOCATE LastDataLine, 3: COLOR 14
PrintHex Temp&
SuperHex VidSegment, LastDataLine, 10, 62,_
Signed(sseg(Array&(0)) + Temp& \ 16), VARPTR(Array&(0)),_
Attrib, FilterOn
IF LOFile& - Temp& < 15 THEN AdjustLastBytes_
LastDataLine
END IF
CASE Null$ + "H" ' Up
IF Pointer& >= 16 THEN
Scroll -1, LastDataLine
Pointer& = Pointer& - 16
ShowHex Pointer&
LOCATE 3, 3: COLOR 14
PrintHex Pointer&
SuperHex VidSegment, 3, 10, 62,_
Signed(sseg(Array&(0)) + Pointer& \ 16), VARPTR(Array&(0)),_
Attrib, FilterOn
END IF
CASE Null$ + "G" ' Home
Pointer& = 0
DrawScreen
CASE Null$ + "O" ' End
Pointer& = LOFile&
CheckPointer Pointer&
CASE "F" ' toggle filter
FilterOn = NOT FilterOn
DrawScreen
LOCATE 25, 58: COLOR 4, 3
IF FilterOn THEN PRINT CHR$(251); ELSE PRINT " ";
CASE ELSE
END SELECT
LOOP UNTIL I$ = CHR$(27)
COLOR 7, 0: CLS
PRINT "Thanks for trying Browser 1.0!"
PRINT
END
REM $STATIC
SUB AdjustLastBytes (Row)
Remov = 16 - (LOFile& AND 15)
LOCATE Row, 58 - Remov * 3
PRINT SPACE$(Remov * 3 + 1);
LOCATE , 78 - Remov
PRINT SPACE$(Remov);
END SUB
SUB CheckPointer (Num&)
IF Num& + PageSize - 15 > LOFile& THEN
Num& = LOFile& + 15 - PageSize
END IF
IF Num& < 0 THEN Num& = 0
DrawScreen
END SUB
SUB DrawInterface
COLOR 14, 12
PRINT " Browser 1.0 ■ by Victor Yiu, July 1993 ■ Idea from"+_
" Calvin French's HEXVIEW "
COLOR 15, 1
PRINT CHR$(218); CHR$(196); CHR$(180);
COLOR 15, 3: PRINT " "; COMMAND$; " "; : COLOR 15, 1
PRINT CHR$(195); STRING$(80 - POS(0), 196); CHR$(191)
FOR Lin = 3 TO LastDataLine
LOCATE Lin, 1: PRINT CHR$(179); SPACE$(78);
IF Lin = 3 THEN
PRINT CHR$(24);
ELSEIF Lin = LastDataLine THEN
PRINT CHR$(25);
ELSE
PRINT CHR$(176);
END IF
NEXT
LOCATE 4, 80, 0: PRINT CHR$(219);
LOCATE 24, 1: PRINT CHR$(192); CHR$(196); CHR$(180);
COLOR 13: PRINT " ( )/"; LTRIM$(STR$(LOF(1)));
COLOR 15: PRINT " "; CHR$(195); STRING$(80 - POS(1), 196);_
CHR$(217);
LOCATE 25, 1: COLOR 14, 3
PRINT " Adjust Viewport "; : COLOR 15
PRINT "[PgUp/PgDn] [Up/Down] [Home/End] [F]=Filter"+_
" [Esc]=Quit ";
END SUB
SUB DrawScreen
STATIC NotOnePage, L ' 1=True, 2=False, 0=first call
IF NotOnePage = 0 THEN
IF LOFile& - 16 < PageSize THEN
NotOnePage = 1
L = (LOFile& - 1) \ 16 + 3
ELSE
NotOnePage = 2
L = LastDataLine
END IF
END IF
ShowHex Pointer&
COLOR 14
Temp& = Pointer&
FOR Row = 0 TO L - 3
SuperHex VidSegment, Row + 3, 10, 62, Signed(sseg(Array&(0))_
+ Temp& \ 16), VARPTR(Array&(0)), Attrib, FilterOn
LOCATE Row + 3, 3, 0
PrintHex Temp&
Temp& = Temp& + 16
NEXT
IF Temp& > LOFile& THEN AdjustLastBytes L
END SUB
FUNCTION GetAdapterSeg
DEF SEG = 0
ColorM = (PEEK(&H410) AND 48) <> 48
DEF SEG 'Monocrome--^ ^
'Color Graphics Adapter or better --|
IF ColorM THEN GetAdapterSeg = &HB800 ELSE GetAdapterSeg = &HB000
END FUNCTION
SUB LoadFile (LOFile&)
Dummy& = FRE("")
TempStor$ = SPACE$(Block)
Start& = sseg(Array&(0))
FOR LoadUp = 1 TO LOFile& \ Block
GET #1, , TempStor$
MemCopy sseg(TempStor$), SADD(TempStor$), Signed(Start&), VARPTR(Array&(0)), Block
Start& = Start& + Block \ 16
ShowHex LoadUp * 1& * Block
NEXT
TempStor$ = SPACE$(LOFile& MOD Block)
GET #1, , TempStor$
MemCopy sseg(TempStor$), SADD(TempStor$), Signed(Start&), 0, LEN(TempStor$)
' *** SSEG to sseg for QB/QBASIC users!
END SUB
SUB PrintHex (Num&)
'PRINT MID$(HEX$(Num& + &H100000), 2);
PRINT RIGHT$("0000" + HEX$(Num&), 5);
END SUB
SUB ShowHex (Posit&) STATIC
COLOR 15, 1
LOCATE 24, 5
PRINT USING "######"; Posit&;
LOCATE , 12
PrintHex Posit&
LOCATE LastBox, 80
PRINT CHR$(176);
LastBox = Posit& * 18 \ LOFile& + 4
LOCATE LastBox, 80
PRINT CHR$(219);
END SUB
FUNCTION Signed (Num&)
IF Num& > 32767 THEN
Signed = Num& - 65536
ELSE
Signed = Num&
END IF
END FUNCTION
'
Msg #: 693 QUIKBAS Subboard
From: VICTOR YIU Sent: 07-12-93 13:40
To: ALL Rcvd: -NO-
Re: BROWSER 1.0 [OBJ] 1/1
' =========== Notice: this PostIt! script can't be extracted
' by the new version of PostIt! posted by Rich. You'll have to
' run this through QB.
'
DEFINT A-Z:DIM SHARED B,K,S,B&,Z&:XA '** by PostIt! 7.0 **
SUB XA:OPEN "O",1,"SUPERHEX.OBJ",4^6:Z&=321:?STRING$(50,177);
U"&O/%-%xzuj#wmj'*se,%%%)ht#ijigd,%7f%%''&(,_I%%%&-x%zujw.mj'%%%%
U",r%jrht:u(+%%%+xh%wtqqBI%%&,oj%%%zZKeC%&'=k7m8C),uuo\JYYX;>EL
U"/%Y5U-ZsZ\-3pJT\(tRAM+%DjG%z%kz%z%kz%z%kz%z%kz%z%kz%z%kz%z%kz%z
U"%bzd?275%4E;M_%]*QK.]EqN*z?[md*f)Sz[3fZ-5=pJ\&WSZs-B25%Y-U3,.B6
U"UU_o05%-=%op_E?M(_a';__\M''[j&aYuPY\zY<#z)E;zRr.nC)[Qfd(z[3L.-D
U",,C5%:zZeCV%&uZRs+=M4->E16JbB&Pslt.e-D,C#/%zZ3eYM+B1&+OAM-%Cs'w
U"=27''3qF;F5,BC)%R&Y'%%&C"
END SUB
CLOSE:?:IF S=171AND B&=Z&THEN?":) Ok!"ELSE?":( Bad!
SUB U(A$):FOR A=1TO LEN(A$):C=ASC(MID$(A$,A))-37:B=B-1:IF C<0THEN
C=91+C*32
S=(S+C)*2:IF B<0THEN B=4:K=C ELSE?#1,CHR$(C+(K MOD
3)*86);:K=K\3:B&=B&+1
S=S\256+(S AND 255):NEXT:LOCATE,1:?STRING$(B&*50\Z&,219);:END SUB
' To make a LIB and QLB out of this file:
'
' LIB SUPERHEX +SUPERHEX;
' LINK /QUI SUPERHEX.LIB,,,c:\qb\bqlb45;
' qbxqlb for PDS (I think)
' vbdosqlb for VBDOS
' Load into QB:
' QB BROWSER /L SUPERHEX /AH
'
' Instructions on how to make EXE file is in the BASIC source.
'
'
Msg #: 694 QUIKBAS Subboard
From: VICTOR YIU Sent: 07-12-93 13:43
To: ALL Rcvd: -NO-
Re: BROWSER 1.0 [ASM] 1/2
; All:
;
; This code is different from my original posting of SuperHex
; very slightly only. I added a mem. copy and a scrolling
; procedure.
; -- Victor
; =========== SuperHEX 1.1 ===============
; by Victor Yiu, July 1993
;
; Ultra-fast ASCII to HEX conversion...
; designed for calling up from a file
; or memory viewer to display hex/ascii
; codes like Norton's DiskEdit, or PCTool's
; View... except much faster!
;
; This code is released into public domain.
; =========================================
CODE SEGMENT PARA PUBLIC 'CODE'
PUBLIC SuperHex, MemCopy, Scroll
ASSUME CS:code, DS:nothing, ES:nothing, SS:nothing
CharsPerLine = 80 ; set-up hard coded constant
FilterReplaceChar EQU '.' ; replace bad chars. with what?
SuperHex PROC FAR
PUSH BP ; set up stack frame
MOV BP, SP
PUSH DS ; save registers
PUSH SI
PUSH DI
; =============================================================
;VidSeg, Row, OffsetHex, OffsetASCii, Seg:Off memory (16 byte)
; BP+20 BP+18 BP+16 BP+14 BP+10 [DWORD]
; BG Color Filter
; BP+8 BP+6
; =============================================================
LES AX, SS:[BP+18] ; VidSeg --> ES
DEC AX ; Row --> AX (adjust to 0-24 range)
JZ NoMul ; if 0, don't multiply to save time
MOV BL, CharsPerLine; get # chars per line
MUL BL ; multiply
NoMul:
SHL AX, 1 ; *2: vid.mem. alternates ASCii/color
MOV DX, AX ; save into DX
LDS SI, SS:[BP+10] ; get source memory into DS:SI
PUSH SI ; save it for later
; ====== Setup to write the 16 bytes of ASCii first
MOV AH, SS:[BP+8] ; get attribute into AH
MOV DI, DX ; move start of row offset into DI
MOV BX, SS:[BP+14] ; get offset of ASCii
DEC BX
SHL BX, 1 ; *2 because of vid. mem.
ADD DI, BX ; compute final offset
CMP WORD PTR [BP+6], 0 ; filter on?
JNE FilterOn
REPT 16 ; repeat 16 times
LODSB ; get byte
STOSW ; store byte + attribute
ENDM
JMP SHORT Continue
EVEN
FilterOn:
MOV CX, 16
MOV BX, '~ ' ; preload constants
EVEN
FilterTop:
LODSB
CMP AL, BL ; below 32?
JL NoShow
CMP AL, BH ; more than 127
JG NoShow
STOSW
LOOP FilterTop
JMP SHORT Continue
EVEN
NoShow:
MOV AL,FilterReplaceChar
STOSW
LOOP FilterTop
; ======= Set up for HEX conversion to screen
Continue:
MOV BX, SS:[BP+16] ; get offset of HEX
DEC BX
SHL BX, 1 ; *2 for vid. mem
ADD DX, BX ; add to original row offset
MOV DI, DX ; put it into the index register
POP SI ; get previous SI
MOV CX, 16 ; do sixteen characters
MOV BL, AH ; attribute into BL
MOV DX, (256*9) + ('A'-'9'-1)
MOV BP, '00' ; preload stuff to make it scream
; BP = '00'
; BL = attribute
; BH = -- reserved for temporary digit
; DL = 'A'-'9'-1
; DH = 9
EVEN
LoopTop:
MOV BH, 16 ; load divisor
LODSB ; get character
MOV AH, 0 ; clear AH
DIV BH ; to get tens in AL, ones in AH.
CMP AL, DH ; > '9'?
JLE NextDigit ; no -- don't fix
EVEN
ADD AL, DL ; fix it
NextDigit:
CMP AH, DH ; > '9'?
JLE WriteOut ; no -- don't fix
ADD AH, DL ; fix it
WriteOut:
ADD AX, BP ; add '00' to digits to make them ASCii
MOV BH, AH ; save ones digit for next character
MOV AH, BL ; get attribute
STOSW ; write digit
MOV AL, BH ; get next
STOSW ; write
MOV AL, ' ' ; write space
STOSW
CMP CX, 9 ; between the 8th and 9th HEX digits
JE AddSpace
LOOP LoopTop
JMP SHORT OttaHere
EVEN
AddSpace: STOSW
LOOP LoopTop
OttaHere:
POP DI ; restore registers
POP SI
POP DS
POP BP
RET 16 ; shave off 16 bytes of passed in parameters
SuperHex ENDP
MemCopy PROC FAR
PUSH BP
MOV BP, SP ; set up stack frame
PUSH DS
PUSH SI
PUSH DI
CLD
MOV CX, [BP+6] ; # to copy in CX
LES DI, [BP+8] ; get dest.
LDS SI, [BP+12] ; get source
SHR CX, 1 ; odd byte
JNC CopyStart
MOVSB
CopyStart: REP MOVSW ; do copy
POP DI
POP SI
POP DS
POP BP
RET 10
MemCopy ENDP
Scroll PROC FAR
PUSH BP
MOV BP, SP ; set up stack frame
; BP+8 = MoveUp? BP+6 = attribute
MOV BH, [BP+6] ; load up attribute
MOV AX, 0601h ; 1 line
CMP BYTE PTR [BP+8], 0 ; zero?
JE Down ; yes; go up
INC AH ; go down (AX=0701)
Down:
MOV CX, 0202h ; (3,3) top left
MOV DX, 0164Ch ; (23,77) bottom right
INT 010h ; call vid. interrupt
POP BP
RET 4
Scroll ENDP
CODE ENDS
END
'
From: AARON LAPIKAS Sent: 07-07-93 17:02
To: MARK PRUITT Rcvd: -NO-
Re: CRCS
Hi Mark!
> My questions are:
Only three? :)
> Does a '32 bit CRC' turn out the same no matter who's code generates it?
> In other words... is there only ONE '32 bit CRC' for a given chunk o' data?
As far as I know, yes. If it weren't that way, how could it be used as
a method of error checking?
> Will the 32 bit CRC of my name turn out completely unique from the other
> several hundred users of the same bbs? Since I have NO idea of even what a
> 32 bit CRC is, much less the math that goes into it, it seems to me that a
...
Yes. The chances of having two different pieces of data with the same
CRC are very small, if not next to none.
> Could someone post "their way of calculating a 32 bit CRC" with a
> layman's (read that: lame brain's) explanation of what the code is
> doing?
'-----------------------------------------------------------------------
DECLARE FUNCTION CRC32% (Target$)
DEFINT A-Z
FUNCTION CRC32 (B$)
DIM CRC AS LONG
CRC = 0
FOR I = 1 TO LEN(B$) 'Calculate for Length of
Block
ByteVal = ASC(MID$(B$, I, 1))
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 128) = 128)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 64) = 64)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 32) = 32)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 16) = 16)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 8) = 8)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 4) = 4)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 2) = 2)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
TestBit = ((CRC AND 32768) = 32768) XOR ((ByteVal AND 1) = 1)
CRC = ((CRC AND 32767&) * 2&)
IF TestBit THEN CRC = CRC XOR &H8005&
NEXT I
CRC32% = CRC
CRCHigh% = (CRC \ 256) 'Break Word Down into
Bytes
CRCLow% = (CRC MOD 256) 'for Comparison Later
ComputeCRC& = CRC 'Return the Word Value
END FUNCTION
'-----------------------------------------------------------------------
'
From: STEVE DEMO Sent: 07-05-93 15:32
To: PAUL SENECHKO Rcvd: -NO-
Re: (R)DIRECTORIES IN QB 4.
-=> Quoting Bill Smith to Paul Senechko <=-
PS>Hi all, I am looking for a way in Quick Basic 4.5 to put all of the
PS>directories on the current drive into a file. Any ideas?
BS> How about SHELL TREE>file.nam
Ha, Ha, Ha, Ha, Ha, That's a good one. Now Listen up Paul I didn't
write this it's about as HOT as the day is :-). I converted it back to
QB for ya.
Steve Demo
'$INCLUDE: 'qb.bi'
DECLARE SUB Tree (drive$, Count!, Array$())
DEFINT A-Z
CONST DOS = &H21
CONST SetDTA = &H1A00, FindFirst = &H4E00, FindNext = &H4F00 ' These
Const are for Tree sub
DIM Array$(500)
Tree "C:\", Count!, Array$()
FOR x = 1 TO Count! STEP 1
IF Array$(x) = "" THEN END
PRINT Array$(x)
NEXT x
SUB Tree (drive$, Count!, Array$())
DIM r AS RegTypeX
search$ = drive$ 'reassign string
since it
IF Count! = 0 THEN ' gets changed in
routine
search$ = UCASE$(search$) 'make upper case
IF LEN(search$) = 1 THEN search$ = search$ + ":" 'define search
search$ = LEFT$(search$, 2)
Count! = 1
Array$(Count!) = search$ + "\"
END IF
zero$ = CHR$(0): DTA$ = SPACE$(43) '43 'define ZERO and DTA
search$ = search$ + "\"
srch$ = search$ + "*" + zero$ 'dos requires zero terminated
string
'get original dta
r.ax = &H2F00
INTERRUPTX &H21, r, r
sgmt = r.es: ofst = r.bx 'save segment and offset of dta
mode = &H4E00 'set mode to FINDFIRST
'set our dta
DO
r.ax = &H1A00
r.ds = VARSEG(DTA$): r.dx = SADD(DTA$) 'change SSEG to VARSEG in qb
INTERRUPTX &H21, r, r 'tell dos where this dta is
r.ax = mode 'findfirst, or findnext
r.cx = 16 'look for directories
r.ds = VARSEG(srch$): r.dx = SADD(srch$)'change SSEG to VARSEG in qb
INTERRUPTX &H21, r, r 'find one
IF (r.flags AND 1) THEN EXIT DO 'if none, bail
f.attr = ASC(MID$(DTA$, 22)) 'attribute in f.attr
tmp$ = MID$(DTA$, 31) + zero$
f.name$ = LEFT$(tmp$, INSTR(tmp$, zero$) - 1) 'directory name in
f.name
mode = &H4F00 'change mode to FINDNEXT
IF ASC(f.name$) <> 46 THEN 'we don't want '.' or '..'
IF f.attr = 16 THEN 'make sure it's a directory
Count! = Count! + 1 'increment Count!
s$ = search$ + f.name$ 'full path name
Array$(Count!) = s$ 'add to array
Tree s$, Count!, Array$() 'look for some dirs here
END IF
END IF
LOOP
r.ax = &H1A00
r.ds = sgmt: r.dx = ofst 'return original dta segment &
offset
INTERRUPTX &H21, r, r
END SUB
'
'Msg #: 730 QUIKBAS Subboard
' From: SCOTT WUNSCH Sent: 07-11-93 10:48
' To: ALL Rcvd: -NO-
' Re: READBAS1.BAS
'
'Salutations, All!
'
' This might come in handy to someone here...
'
' Area: NET140.TECH
' From: Frank Cox, 1:140/53 (10 Jul 93 10:18)
' To: Whoever cares
' Subj: READBAS1.BAS
'__________O_/________________/ SNIP \__________________\_O__________
' O \ \ HERE / / O
'This just came around in the DR_DEBUG echo and I thought it was worth
'passing along.
'
'Problem: You have some GWBASIC source code which has been saved in
'tokenized format, and you don't have a copy of GWBASIC to use to
'convert it into an ASCII file for use under QBASIC or whatever. This
'is a common problem as GWBASIC is not distributed with MS-DOS 5.0 and
'up.
'Solution: This old program (which appears to run fine under QBASIC):
1 '' READBAS 1.1 - READS BASIC PROGRAMS SAVED IN BINARY
2 '' NELSON FORD (713) 960-1300 (713) 721-6104 APRIL 11,1985
3 ''
4 '' PUBLIC DOMAIN. The idea is to compile this program and use it while in
5 '' DOS to look at BASIC programs that have been saved in binary format. The
6 '' compiled version of this is not being uploaded due to the inordinate
7 '' amount of difficulty and expense required to make a go
10 DEFINT A-Z: CLS: INPUT "FILE NAME"; FI$
20 INPUT "TO (1)SCREEN (2)PRINTER (3)DISK"; D
30 IF D=1 THEN F2$="SCRN:" ELSE IF D=2 THEN F2$="LPT1:" ELSE IF D=3
THEN INPUT_ "OUTPUT FILENAME"; F2$ ELSE 20
40 DIM X#(8): PRINT: PRINT "PRESS ANY KEY TO ABORT": PRINT
50 DIM T$(115), T3$(6), T4$(30), T5$(37)
60 FOR T=129 TO 243: READ T$(T-128): NEXT 'tokens 129-243
70 FOR T=129 TO 134: READ T3$(T-128): NEXT 'token 253 followed by 129-134
80 FOR T=129 TO 158: READ T4$(T-128): NEXT 'token 254 followed by 129-158
90 FOR T=129 TO 165: READ T5$(T-128): NEXT 'token 255 followed by 129-165
95 '
100 OPEN FI$ AS 1 LEN=1: FIELD 1, 1 AS X$
110 OPEN F2$ FOR OUTPUT AS #2: GET 1
120 IF ASC(X$) <>255 THEN PRINT "NOT A BASIC PROGRAM SAVED IN BINARY":END
125 '----get, print line number:
130 GET 1: X=ASC(X$): GET 1: IF X=0 AND ASC(X$)=0 THEN STOP
140 GET 1: N$=STR$(ASC(X$)): GET 1: X=ASC(X$)
150 IF X>0 THEN N$=STR$(X*256+VAL(N$))
160 PRINT #2, RIGHT$(N$,LEN(N$)-1) " ";
190 '----get a hex character and translate:
200 GET 1: X= ASC(X$)
210 U$=INKEY$: IF U$<>"" THEN END
220 IF X=58 THEN GET 1: X=ASC(X$): IF X=143 THEN GOSUB 910: GOTO 130 ELSE IF _ X<>161 THEN PRINT #2,":";
230 IF X=0 THEN PRINT #2,"": GOTO 130 'ascii 0 marks end of BASIC line
240 IF X>31 THEN 300 ELSE IF X <11 THEN STOP
250 ON X-10 GOSUB
400,440,480,500,540,580,600,600,600,600,600,600,600,600, 600,600,640,660,720,815 ,820
260 GOTO 200
270 RETURN
290 '------
300 IF X <128 THEN PRINT #2, X$;: GOTO 200
310 IF X >128 AND X <244 THEN PRINT #2, T$(X-128);: GOTO 200
320 IF X >252 AND X <256 THEN GET 1: Y=ASC(X$) ELSE 200
330 IF Y <129 THEN PRINT "ERROR IN FILE": STOP
340 ON X-252 GOTO 350,360,370: GOTO 200
350 PRINT #2, T3$(Y-128);: GOTO 200
360 PRINT #2, T4$(Y-128);: GOTO 200
370 PRINT #2, T5$(Y-128);: GOTO 200
390 '
400 GET 1: N=X: GET 1: N=X*256 +N '11 = OCTAL
410 PRINT #2, "&O" OCT$(N);
420 RETURN
430 '
440 GET 1: N=X: GET 1: N=X*256 +N '12 = HEX
450 PRINT #2, "&H" HEX$(N);
460 RETURN
470 '
480 STOP '13 NOT USED
490 '
500 GET 1: N$=STR$(ASC(X$)) '14 INTEGERS
505 GET 1: X=ASC(X$)
510 IF X>0 THEN N$=STR$(X*256+VAL(N$))
520 PRINT #2, RIGHT$(N$,LEN(N$)-1);
530 RETURN
535 '
540 GET 1: N$=STR$(ASC(X$)) '15 = NUMBERS 10 TO 255
550 PRINT #2, RIGHT$(N$,LEN(N$)-1);
560 RETURN
570 '
580 STOP '16 NOT USED
590 '
600 N$=STR$(X-17) '17 - 26 = NUMBERS 0 TO 9
610 PRINT #2, RIGHT$(N$,LEN(N$)-1);
620 RETURN
630 '
640 STOP '27 NOT USED
650 '
660 GET 1: N=ASC(X$): GET 1 '28 = NUMBERS > 255 AND <32267
670 N$= STR$(256*ASC(X$) +N)
680 PRINT #2, RIGHT$(N$,LEN(N$)-1);
690 RETURN
700 '
710 '29 = NUMBERS >32267 AND < ?
720 FOR I=1 TO 4: GET 1: X#(I)=ASC(X$): NEXT: Z$=""
730 FOR J=3 TO 1 STEP -1: Y#=X#(J)
740 FOR I= 7 TO 0 STEP-1
750 IF Y# > 2^I-1 THEN Z$=Z$+"1": Y#=Y#-2^I ELSE Z$=Z$+"0"
760 NEXT
770 NEXT: N#=1: Z$=RIGHT$(Z$,23)
780 FOR I=1 TO 23: N#= N# + VAL(MID$(Z$,I,1)) * .5^I: NEXT
790 N$=STR$(N# * 2^(X#(4)-129)): PRINT #2, RIGHT$(N$,LEN(N$)-1); "!";
800 RETURN
815 ' 30 NOT USED
816 '
819 ' 31 = DOUBLE PRECISION
820 FOR I=1 TO 8: GET 1: X#(I)=ASC(X$): NEXT: Z$=""
830 FOR J=7 TO 1 STEP -1: Y#=X#(J)
840 FOR I= 7 TO 0 STEP-1
850 IF Y# > 2^I-1 THEN Z$=Z$+"1": Y#=Y#-2^I ELSE Z$=Z$+"0"
860 NEXT
870 NEXT: N#=1: Z$=RIGHT$(Z$,55)
880 FOR I=1 TO 55: N#= N# + VAL(MID$(Z$,I,1)) * .5^I: NEXT
890 N$=STR$(N# * 2^(X#(8)-129)): PRINT #2, RIGHT$(N$,LEN(N$)-1); "#";
900 RETURN
905 ' read from ' to end of line:
910 PRINT #2, "'";: GET 1:
920 GET 1: IF ASC(X$) >0 THEN PRINT #2, X$;: GOTO 920
950 PRINT #2, "": RETURN
955 '
960 'tokens 129-244:
970 DATA
END,FOR,NEXT,DATA,INPUT,DIM,READ,LET,GOTO,RUN,IF,RESTORE,GOSUB,RETURN
980 DATA
REM,STOP,PRINT,CLEAR,LIST,NEW,ON,WAIT,DEF,POKE,CONT,NU,NU,OUT,LPRINT
990 DATA LLIST,NU,WIDTH,ELSE,TRON,TROFF,SWAP,ERASE,EDIT,ERROR,RESUME,DELETE
1000 DATA AUTO,RENUM,DEFSTR,DEFINT,DEFSNG,DEFDBL,LINE,WHILE,WEND,CALL,NU,NU,NU
1010 DATA WRITE,OPTION,RANDOMIZE,OPEN,CLOSE,LOAD,MERGE,SAVE,COLOR,CLS,MOTOR
1020 DATA BSAVE,BLOAD,SOUND,BEEP,PSET,PRESET,SCREEN,KEY,LOCATE,NU,TO,THEN,TAB(
1030 DATA STEP,USR,FN,SPC,NOT,ERL,ERR,STRING$,USING,INSTR,"'",VARPTR,CSRLIN
1040 DATA POINT, OFF,INKEY$,NU,NU,NU,NU,NU,NU,NU,>,=,<,+,-,*,/,^,AND,OR,XOR,EQV
1050 DATA IMP,MOD
1060 'pre-token 253, tokens 129-134:
1070 DATA CVI,CVS,CVD,MKI$,MKS$,MKD$
1080 'pre-token 254, tokens 129-158:
1090 DATA FILES,FIELD,SYSTEM,NAME,LSET,RSET,KILL,PUT,GET,RESET,COMMON,CHAIN
1100 DATA DATE$,TIME$,PAINT,COM,CIRCLE,DRAW,PLAY,TIMER,ERDEV,IOCTL,CHDIR,MKDIR
1110 DATA RMDIR,SHELL,ENVIRON,VIEW,WINDOW,PMAP
1120 ' pre-token 255, tokens 129-165:
1130 DATA LEFT$,RIGHT$,MID$,SGN,INT,ABS,SQR,RND,SIN,LOG,EXP,COS,TAN,ATN,FRE
1140 DATA INP,POS,LEN,STR$,VAL,ASC,CHR$,PEEK,SPACE$,OCT$,HEX$,LPOS,CINT,CSNG
1150 DATA CDBL,FIX,PEN,STICK,STRIG,EOF,LOC,LOF
'
OBJECT ORIENTED BASIC
Possibility or Pipe Dream?
an explorative article
TABLE OF CONTENTS
1.0 Introduction
1.1 Key Terminology and Concepts
2.0 BASIC-Specific Considerations of Object Paradigm Implementation
2.1 Standardization of Terms in Object Oriented BASIC
2.2 An Introduction to Advanced Topics in OOP
3.0 Closing Notes
=======================================================================
1.0 Introduction
BASIC has evolved from the time-sharing "Beast of Dartmouth" into a
powerful, structured language fit for the programming needs of the
nineties. Despite this evolution, however, major software compiler
developers have failed to introduce object oriented extensions into
the language.
This article will explore some possible extensions to modern
BASIC that would truly expand the language. Since, because of its
nature, this article will use a speculative approach, the reader
should bear in mind that no particular implementation is being
suggested as the "best" way to bring object-orientation to
BASIC. Moreover, some BASIC programmers may feel that certain low
level features such as in-line assembler and more diverse data
types should be introduced into the BASIC language before object-
orientation is even considered. These readers should remember the
theoretical nature of this discussion, and leave all such
preferences out of the exploration at hand.
1.1 Key Terminology and Concepts
First, I must define some key terms and concepts. My use of the generic
term BASIC (Beginner's All-purpose Symbolic Instruction Code) will,
unless otherwise stated, refer to the Microsoft QuickBASIC v4.5 dialect
of BASIC, since this represents a widely accepted implemenation of
modern, structured BASIC. The term OOP (Object Oriented Programming)
will be used to refer to those programming practices that rely on the
object paradigm. Although the terminology differs from compiler to
compiler, the object oriented paradigm is considered by modern usage to
intrinsically encompass the following concepts, to be defined later:
1. Encapsulation
2. Inheritence
3. Polymorphism
4. Overloading
Therefore, when I say that a given concept is "object oriented" I
specifically mean that it involves the above four concepts.
Other important terms that cannot be ignored in any discussion of OOP,
due to their repeated use in the discussion of such are:
5. Class
6. Method (or Member Function)
7. Object (or Class Instance)
8. Information or Data Hiding
Not able to decide which term to define first, I will begin with a
general overview fo the underlying philosophy of OOP.
In classical structured programming, data and code are considered
separate entities. Code manipulates data. Data fuels code. For
example, wanting to implement a graphics font engine, a classical
BASIC programmer might read a list of DATA statements into a globally
accessible array, and then have a series of globally accessible
SUBPROGRAMS manipulate those raster data on the screen in such a
way as to produce the desired visual effect. The problem with this
approach is that both the data and the related code are equally
accessible, and they are only loosely cohesive. Wanting to enhance
code written by a colleague, a second programmer will encounter
data structures that he should neither modify nor even poll, but
that may not always be possible. Having modified an essential
data structure, the second programmer may introduce errors
into the whole underlying logic of the system.
For instance, suppose the original programmer had defined the
font data structure thus:
TYPE FontDataType
FontName AS STRING * 12
FontPointSize AS INTEGER
RasterDataPtr AS LONG
END TYPE
Now, looking at this, Programmer Two decides that he can avoid a
FUNCTION call to funGetFontPointSize() by just reading the value of
Font.FontPointSize directly. Programmer Two alters his code to
access this variable directly, and in doing so, avoids what he
considers costly calls to funGetFontPointSize(). He is
promoted to another department (presumably for having sped up the
code of his predecessor). Enter Programmer Three. Quite within
his bounds, he discovers that point size is never greater than 255,
and so redefines the whole structure to account for this, thereby
reducing overall memory consumption by one byte:
TYPE FontDataType
FontName AS STRING * 12
FontPointSize AS STRING * 1
RasterDataPtr AS LONG
END TYPE
Of course, being conscientious, he modifies funGetFontPointSize()
to account for this innovation. He compiles the program. It crashes.
Why? Because, this is now an illegal statement:
Font.FontPointSize = 12
What to do? He must use his search and replace to go through the
entire program and change all such instances to:
Font.FontPointSize = CHR$(12)
Or, he can forget his alterations altogether.
In BASIC, there is no INFORMATION HIDING that would prevent such
problems from occuring. Since FontPointSize is a public member
of FontDataType, Programmer Two was well within his rights to do
as he saw fit as far as accessing it. Had the original programmer
had an object oriented BASIC, however, he could have prevented the
entire problem with little difficulty by making FontPointSize a
PRIVATE data member of the CLASS font. This might have looked
similar to this:
CLASS FontClass
FontName AS PUBLIC STRING * 12
FontPointSize AS PRIVATE INTEGER
RasterDataPtr AS PRIVATE LONG
funGetFontPointSize AS PUBLIC FUNCTION
subSetFontPointSize AS PUBLIC SUB
END CLASS
DIM Font AS FontClass
[Please bear with the strange new syntax, since it will be covered
in more detail in section 2.0.]
Now, the only way to access Font.FontPointSize is indirectly. This
would NOT work, since this data member is now PRIVATE:
Font.FontPointSize = 12
This, then, would be the ONLY way to achieve such a thing:
Font.subSetFontPointSize 12
In the above example, the item Font is what is called a CLASS INSTANCE.
That is to say, Font is "an instance of the class FontClass." This is
what is commonly called an OBJECT, and it is from this that we arrive at
the phrase "object oriented" programming.
Now, when Programmer Two comes along, he CANNOT pull off his stunt,
and he is not promoted to another department. Programmer Three comes
along, and sees room for improvement and redefines the class thus:
CLASS FontClass
FontName AS PUBLIC STRING * 12
FontPointSize AS PRIVATE STRING * 1
RasterDataPtr AS PRIVATE LONG
funGetFontPointSize AS PUBLIC FUNCTION
subSetFontPointSize AS PUBLIC SUB
END CLASS
Since all calls to change FontPointSize are through the centralized
subSetFontPointSize, Programmer Three just modifies that a bit, and
earns himself a nice raise in salary for shaving a byte off the
memory requirements of the structure.
Consider the above example. The data are:
1. FontName
2. FontPointSize
The code portions (called MEMBER FUNCTIONS or METHODS, since they
are "methods of acting upon or accessing" the data) are:
1. funGetFontPointSize
2. subSetFontPointSize
Since it is unlikely that subSetFontPointSize will ever be needed for
anything other than the setting of FontPointSize, it makes sense to
bind the code to the data it works with. This binding is called
ENCAPSULATION.
Having examined these more essential terms, there is the issue of
OVERLOADING. Although not object oriented in the strictest sense,
it does aid in generalizing classes to an extent that they can
operate upon different types of data.
Consider the following:
subQuickSort A%()
Now, in classical BASIC programming, if we wanted to sort anything
other than INTEGER arrays, we would have to write another SUBPROGRAM
and modify the algorithm to account for this new data type. This
SUBPROGRAM would have to be named something other than subQuickSort.
For example:
subQuickSortSTR A$()
might be used for STRING arrays, and
subQuickSortLONG A&()
might be used for LONG INTEGER arrays. And, of course, should a
programmer ever want to sort a user-defined TYPE array:
subQuickSortUserTYPE UserArray()
would be the only way to do it.
But, consider the above. All of these routines do the same thing. It
seems a waste to have three names to do what amounts to the same thing:
sorting arrays. The answer is to "overload" a SUBPROGRAM name with
three corresponding pieces of code. Once subQuickSort is overloaded, it
can do tripple-time thus:
subQuickSort A%()
subQuickSort A$()
subQuickSort UserArray()
Of course, each call invokes DIFFERENT CODE to do the actual sorting,
but this detail is handled by the compiler in a transparent fashion.
The programmer's only responsibility would be to provide the code for
each instance of subQuickSort, in the following manner:
SUB subQuickSort (Array AS INTEGER)
|
|
code to sort INTEGER arrays goes here
|
END SUB
SUB subQuickSort (Array AS LONG)
|
|
code to sort LONG INTEGER arrays goes here
|
|
END SUB
SUB subQuickSort (Array AS UserDefinedType)
|
|
code to sort arrays of UserDefinedType goes here
|
|
END SUB
Upon seeing the second instance of subQuickSort in the source listing,
the object oriented BASIC compiler would know that it is dealing with
an overloaded SUBPROGRAM.
Overloading is already done by BASIC compilers, but it is done at a
level not within the control of the programmer. Consider:
PRINT a
PRINT a$
Each case of PRINT prints a different data type. The PRINT statement,
we could say, then, is overloaded. Also to consider is the overloading
of operators such as occurs already in BASIC:
A$ = B$ + C$
A% = B% + C%
The addition operator is serving two masters here. In the first case,
it is being used to concactenate strings. In the second, it is being
used to add two numbers. The processes are internally dissimilar.
How, then, does the BASIC compiler contend with these cases? The
addition operator is overloaded at an internal level. If a programmer
using an object oriented BASIC were to step into the scene, however,
we very well might see this type of overloading of the addition and
assignment operators:
OVERLOAD "+" FOR ArrayOne(), ArrayTwo()
TotalElements = UBOUND(ArrayOne) + UBOUND(ArrayTwo)
DIM ReturnArray(TotalElements)
FOR i = 1 to UBOUND(ArrayOne)
ReturnArray(i) = ArrayOne(i)
NEXT i
FOR q = i + 1 TO i + UBOUND(ArrayTwo)
ReturnArray(q) = ArrayTwo(q-i)
NEXT q
REDIM ArrayOne(TotalElements)
' The following uses an overloaded assingment operator
' whose overload definition follows.
ArrayOne() = ReturnArray()
END OVERLOAD
OVERLOAD "=" FOR ArrayOne(), ArrayTwo()
FOR i = 1 TO UBOUND(ArrayOne)
ArrayOne(i) = ArrayTwo(i)
NEXT i
END OVERLOAD
This bit of sophisticated operator overloading would allow the
programmers to add entire arrays to one another as follows:
NewList() = ListOne() + ListTwo()
For some readers, all this may be a new concept in programming. If
it seems hard to understand, please take time to reread this section
before continuing, since the next part of this discussion relies on
the reader's comprehension of all eight terms pertinent to the object
oriented programming paradigm, which are, again:
1. Encapsulation,
2. Inheritence,
3. Polymorphism,
4. Overloading,
5. Class,
6. Method (or Member Function),
7. Object (or Class Instance),
8. Information or Data Hiding.
[Polymorphism has been purposely avoided for the purposes of this
discussion, due to its rather esoteric nature.]
2.0 BASIC-Specific Considerations of Object Paradigm Implementation
When considering whether BASIC in its present form could
be expanded to include object oriented extensions, we must first look
at what is already possible in standard BASIC. For example, the
following code resembles inheritence, at least in part:
TYPE ColorType
R AS INTEGER
G AS INTEGER
B AS INTEGER
END TYPE
TYPE CoordinateType
X AS INTEGER
Y AS INTEGER
END TYPE
TYPE CircleType
Point AS CoordinateType
Color AS ColorType
Radius AS INTEGER
END TYPE
This is not classical inheritence, but the analogy suffices. Looking
at the syntactical elements of the above code, we see that a similar
structure could easily be adopted for use with CLASS definitions:
CLASS CircleClass
Point AS CoordinateType
Color AS ColorType
Radius AS INTEGER
END CLASS
A question arises, however. The above definition of the CircleClass
CLASS is not executable code, but merely a definition template. It
defines CircleClass, but does not assign a "class instance." That is
to say, there are not yet any objects of CircleClass defined in the
program. Consider this standard BASIC:
TYPE AddressType
Street AS STRING * 10
City AS STRING * 32
State AS STRING * 2
ZIP AS STRING * 12
END TYPE
DIM Envelope AS AddressType
The DIM statement is used to create an instance of a variable
called Envelope that is of the user defined type AddressType. It
makes perfect sense, then, that the DIM statement could be used
in this manner:
CLASS CircleClass
Point AS CoordinateType
Color AS ColorType
Radius AS INTEGER
END CLASS
DIM Orb AS CircleClass
(Remember, having DIM serve this double purpose is known as
overloading the DIM statement.) This syntax serves our purposes
wonderfully, since it does not involve the introduction of completely
foreign operators and follows the present syntactical structure of
standard BASIC.
Another consideration in the creation of classes is the fact that
classes may contain both variables and methods in their definitions,
as shown in the introduction:
CLASS FontClass
FontName AS PUBLIC STRING * 12
FontPointSize AS PRIVATE INTEGER
RasterDataPtr AS PRIVATE LONG
funGetFontPointSize AS PUBLIC FUNCTION
subSetFontPointSize AS PUBLIC SUB
END CLASS
This shows a suggested means of expressing both the scope and the
type of each part of the definition. Note, however, that, although
subSetFontPointSize is defined in this template, there is, as yet,
no code attached to the definition. It is said, in OOP parlance, that
the "the scope of the member function is unresolved." The method is
prototyped, but that is all. In C++, what is known as the "scope
resolution operator" is used to resolve a method, that is, assign
executable code to it. This is done as follows:
void FontClass::subSetFontPointSize (int PointSize)
{
|
code to achieve this end goes here
|
}
Essentially, this translates into the English statement:
"Define funGetFontPoint size of the class FontClass as follows...."
In an attempt to avoid convoluted syntactical introductions into the
BASIC language, what follows is a possible solution:
SUB FontClass.subSetFontPointSize (PointSize AS INTEGER)
|
|
code that assigns the point size goes here
|
|
END SUB
Since the compiler would presumably recognize FontClass as being a
class from the earlier CLASS ... END CLASS block, this should suffice
as a means of resolving the scope of the method subSetFontPointSize,
while avoiding the introduction of :: as a new BASIC operator.
Next comes the issue of overloading both keywords and operators. A
simple extension of BASIC would allow this to be sufficient in the
case of SUBPROGRAMS and FUNCTIONS:
SUB subQuickSort (Array AS STRING)
|
|
END SUB
SUB subQuickSort (Array AS INTEGER)
|
|
END SUB
The second SUB definition would imply overloading. This would be
prototyped at the beginning of the source listing thus:
DECLARE SUB subQuickSort (Array AS STRING)
DECLARE SUB subQuickSort (Array AS INTEGER)
Operators, however, are completely different in that BASIC has
no way of referring to them explicitly. A proposed extension:
OVERLOAD "=" FOR LeftArgument, RightArgument
|
|
definition code goes here
|
|
result returned in LeftArgument
|
|
END OVERLOAD
Of course, the "=" could be any ASCII character or even multiple
ASCII characters. This would allow the object oriented BASIC program
to do this, for example:
OVERLOAD "**" FOR LeftArgument, RightArgument
' Some langauges use ** for raising to a power
LeftArgument = LeftArgument ^ RightArgument
END OVERLOAD
The following, however, would not be possible, since it would involve
late binding and interpreted evaluation at run-time:
OVERLOAD Operator$ FOR LeftArgument, RightArgument
SELECT CASE Operator$
CASE "**"
LeftArgument = LeftArgument ^ RightArgument
|
|
etc.
|
|
END SELECT
END OVERLOAD
2.1 Standardization of Terms in Object Oriented BASIC
Before the discussion continues, perhaps it would be wise to step
aside to establish a set of standard terms. Since certain
OOP concepts carry many different names (ie. "member function" is
also "method") a standard way of refering to any particular device
should be adopted. But, really, this could become quite involved;
what is more appropriate, the term "method" or "member function?"
Perhaps, rather than debate too long and hard on the subject,
Microsoft's terminology as used for Visual Basic should be adopted:
1. OBJECT rather than "class instance"
2. METHOD rather than "member function"
3. PROPERTY rather than "member variable"
For terms not used by Visual Basic, I suggest the following use by
object oriented BASIC:
1. DATA HIDING rather than "information hiding"
2. METHOD DECLARATION rather than "scope resolution"
3. METHOD DECLARATOR rather than "scope resolution operator"
4. OBJECT BINDING rather than "encapsulation"
5. OVERLOADING remains unchanged
6. CLASS remains unchanged
I use these substitutes for the other terms because they have a
BASIC sound to them, whereas the other terms, like "scope resolution
operator" may sound odd to BASIC programmers. DECLARATOR rings of
BASIC's DECLARE statement, thereby reducing the foreigness of the
term METHOD DECLARATOR. (In case you have forgotten, the :: is
the scope resolution operator in C++, whereas the . is used in this
theoretical object oriented BASIC of ours.)
Using this terminology, we have this model:
/ CLASS VectorClass ' This is a CLASS DECLARATION
| X AS PRIVATE INTEGER ' This is a PROPERTY of VectorClass
O B | Y AS PRIVATE INTEGER ' As is this
B I | ' ^^^^^^
J N | ' Use of PRIVATE demonstrates DATA HIDING
E D | ' Whereas use of PUBLIC demonstrates the oposite--\
C I | ' |
T N | ' /-------------------------------/
G | ' VVVVVV
| subSetVector AS PUBLIC SUB ' This is a METHOD
\ END CLASS
' This operator is the METHOD DECLARATOR in this context
' |
' V
D / SUB VectorClass.subSetVector ( X AS INTEGER, Y AS INTEGER )
E |
M C |
E L |
T A |
H R |
O A |
D T |
I |
O |
N \ END SUB
2.2 An Introduction to Advanced Topics in OOP
To this point, most fundemental concepts of the object oriented
paradigm have been examined. The reader should have a concept of
class, object binding, method declaration, overloading, and
data hiding, and should also understand the essence of how these
object oriented extensions may be added to BASIC.
There are other considerations, however. When an object is created,
for instance, how is it initialized? That is to say, how are its
properties set to appropriate starting values? A typical standard
BASIC program might accomplish this thus:
CALL subFontInit()
This is fine, but remember that there can be more than one OBJECT of
the same CLASS as in this case:
DIM Helvetica AS FontClass
DIM Courier AS FontClass
DIM TimesRoman AS FontClass
Now, to initialize the data for each of these, we must do something
like this:
CALL subFontHelveticaInit
CALL subFontCourierInit
etc.
In C++, there is away around this that we can adopt for BASIC use.
In every class in C++ there is an implied "constructor." This is
a new term. Essentially, the constructor is a method within the
class definition that is executed whenever an object is created.
For an example of this, consider this method declaration:
SUB FontClass.FontClass
|
|
code to initialize object goes here
|
|
END SUB
(Visual Basic programmers will recognize this as being analogous to
the Load_Form event.) Note that the method declaration uses FontClass
twice. This informs the compiler that it is dealing with the explicit
definition of a CONSTRUCTOR.
In the actual binding declaration of the class, this syntax is
suitable:
CLASS FontType
|
etc.
|
FontType AS CONSTRUCTOR
|
etc.
|
END CLASS
The CONSTRUCTOR type then, signifies that this template will be
followed by a method declaration for a constructor. Now, when the
programmer includes this code:
DIM Helvetica AS FontType
The compiler will include appropriate initialization routines.
Another aspect of this, the "destructor," is exactly the same, except
that it operates after the object falls from scope. (Visual Basic
programmers again will note the analagous use of the Form_Unload event.)
Destructors deinitialize data, cleaning up things when the program ends
execution, for instance. In C++, a special operator is used to indicate
the deconstructor: ~FontClass. This use of the tilde is foreign to
BASIC, however, so perhaps it would be better to introduce another
keyword rather than a new operator:
CLASS FontType
|
etc.
|
FontType AS CONSTRUCTOR
FontType AS DESTRUCTOR
|
etc.
|
END CLASS
Now, the method would simply be declared:
SUB FontType.FontType DESTRUCTOR
|
|
code to deinitialize data structures goes here
|
|
END SUB
This is syntacally familiar to a BASIC programmer in another form:
SUB subPrintToScreen (InText AS STRING) STATIC
|
|
END SUB
The STATIC keyword modifies the nature of the SUBPROGRAM. Consquently,
I have suggested the DESTRUCTOR keyword be used in a similar syntactical
fashion.
3.0 Closing Notes
Indeed, BASIC has evolved from the time-sharing days of Dartmouth.
Despite this evolution, however, major software compiler developers
have failed to introduce object oriented extensions into the language.
Perhaps this article has introduced some new concepts to the reader,
perhaps not. At the very least, it has explored some ways
an object oriented paradigm might be introduced successfully into
BASIC programming with as little pain possible. Programmers tend to
maintain their old programming habbits despite the innovations that
come into their languages, and consequently, any major changes to
the way BASIC operates may prove to be obstacles rather than useful
tools. I feel that my suggestions involve minimal relearning of the
syntax of BASIC, since they adopt the flavor of existing structures.
In the end, though, the question is not what is the better method
or terminology to use, really, but rather:
"Object Oriented BASIC, possibility or pipedream?"
'
From: RICH GELDREICH Sent: 07-15-93 02:59
To: ANDY THOMAS Rcvd: -NO-
Re: (R)CALL INTERRUPT
> LA>DL> AL = AX AND &HFF
> LA>DL> AH = (AX \ &H100) AND &HFF
>
> LA>is very likely the most efficient way to do it. I hope all
> LA>the people posting the wrong way to do it, (AH = AX\256) are
> LA>watching as well.
>
> Well, wrong is not quite the right word, as &H100 = 256 the two
> methods are equivalent. Tacking on the AND &HFF is redundant and will
> only change the value if something is wrong originally with the AX
> value. AX should be made of two bytes, and have a maximum value of
> &HFFFF only if AX is greater than this maximum value will the AND &HFF
> change anything. AH=AX\256 is a perfectly valid calculation and will
> work whenever the AX value is valid. If the AX value is invalid you
> don't want to be using it anyway! :> -Andy
Hmmm. Seems like everybody is wrong one way or another in this
conversation... :-)
First off, let's assume AX = &HFFFF. The correct result of shifting
AX right 8 places would be &HFF. The calculation for doing this above -
AH = (AX\&h100) AND &HFF, would *not* return &HFF, but &h0, because
(-1\&h100) = 0. (Remember QuickBASIC treats 16 bit words as signed, so
&HFFFF = -1.)
In this context, AH=AX\256 is not a perfectly valid calculation. If
AX contains an unsigned integer, this calculation will not work
correctly all of the time.
This calculation does work for all values of AX:
AL = AX AND &HFF
AH = (CLNG(AX) AND &HFFFF&) \ &H100
The "&" after &HFFFF is important.
'
From: CALVIN FRENCH Sent: 07-16-93 00:00
To: ALL Rcvd: -NO-
Re: (1/6) C-SMENU.BAS V1.1
Here's a neat-o-roonie pulldown menu routine that I'm sure SOMEBODY
will find good... nothing too spectacular, but useful anyways.
'C-SMENU1.BAS by Calvin French, 1993
'-------------------------------------------------------------
'This code is entirely PD but if you use it please put my name
'in your program somewhere however you don't have to if you
'don't want to
'-------------------------------------------------------------
DEFINT A-Z
'------ USED BY SUPERMENU
DECLARE SUB PullDownMenu (y1, TopX1(), BoxX1(), BoxX2(),_
LastOpt(), MenuDat$(), Fore, Back, SelectFore, SelectBack,_
hilightfore, hilightback, KeyFore, ReturnX, ReturnY)
DECLARE SUB DrawShadow (x1, y1, x2, y2)
'----------------------------
DECLARE SUB DrawScreen () '<-- these ARE NOT used by SuperMenu
DECLARE SUB DrawBox (x1, y1, x2, y2) '<---'
CONST true = -1, false = 0
'To save space, I'll illustrate how the menu is set up, and then
'just read the rest of the menu data from DATA statments, ok?
REDIM MenuDat$(5, 7)
'
' First element (8): This is the total number of headers that the
' menu has
' Second element (7): This is the highest number of options, including
' separator lines, that your menu has
'
MenuDat$(1, 0) = " File "
'
' The 0th second element of each array is the header.
'
MenuDat$(1, 1) = " ^New Program"
'
' The carat (^) is used to illustrate a hotkey.
' NOTE: YOU MUST SPECIFY ONE HOTKEY PER MENU OPTION!
'
MenuDat$(1, 2) = " ^Open Program"
MenuDat$(1, 3) = " Save ^As"
MenuDat$(1, 4) = "^:"
'
' The "^:" means put a line separator in there.
'
MenuDat$(1, 5) = " ^Print"
MenuDat$(1, 6) = "^:"
MenuDat$(1, 7) = " E^xit"
TopX1(1) = 3
'
' This is the X location where the header (e.g., " File ") is to appear
'
BoxX1(1) = 2
'
' This is the X location where the left hand side of the box is to be
'
BoxX2(1) = 20
'
' This is the X location where the right hand side of the box is to be
'
LastOpt(1) = 7
'
' This is the number of options in the particular menu
'
'Now, to save space, I'll just read the rest of them...
FOR n = 2 TO 5
READ LastOpt(n)
READ TopX1(n)
READ BoxX1(n)
READ BoxX2(n)
FOR x = 0 TO LastOpt(n)
READ MenuDat$(n, x)
NEXT x
NEXT n
'--------------------------------
'Effiecient way of doing toggles:
DIM ToggleChar$(-1 TO 0) 'true false
ToggleChar$(-1) = CHR$(254)
ToggleChar$(0) = " "
Toggle1 = false 'off
Fore = 0 'color for menu
Back = 7
SelectFore = 15 'color for topic bar. QB uses 7, 0 but i like 15, 1
SelectBack = 1
hilightfore = 7 'color for menu bar inside menu
hilightback = 0
KeyFore = 15 'hilight color for hotkeys
y1 = 1 'the Y loc you want the menu to appear on
COLOR 15, 1
PRINT STRING$(2000, 176);
DrawScreen
DO
MenuDat$(4, 4) = ToggleChar$(Toggle1) + "^Toggle 1"
PullDownMenu 1, TopX1(), BoxX1(), BoxX2(), LastOpt(),_
MenuDat$(), Fore, Back, SelectFore, SelectBack, hilightfore,_
hilightback, KeyFore, ReturnX, ReturnY
'ReturnX is the returned X value
'ReturnY is the returned Y value
'
'If you set it up to reroute ReturnX/ReturnY, you will be able to keep
'the menu bar on the option the user last selected... nice...
IF ReturnX = 4 AND ReturnY = 4 THEN Toggle1 = NOT Toggle1
LOOP UNTIL ReturnX = 1 AND ReturnY = 7
COLOR 7, 0
CLS
PRINT "Calvin French, 1993"
PRINT "Seeya!"
END
'Menu data. I did it like this to save space. You don't have to do it like
' this (read it into the array)
PulldownMenuData:
DATA 3,9,8,32," Edit "," Cu^t Shift+DEL"," ^Copy Ctrl+INS"," ^Paste Shift+INS"
DATA 3,15,14,39," View "," ^SUBs... F2"," O^utput Screen F4"," Included ^Lines"
DATA 4,21,20,48," Search "," ^Find..."," ^Change...","^:","^Toggle 1"
DATA 4,72,51,78," Help "," ^Index"," ^Contents"," ^Topic: F1"," ^Help on Help Shift+F1"
SUB DrawBox (x1, y1, x2, y2)
LOCATE y1, x1
PRINT CHR$(218); STRING$(x2 - x1 - 2, 196); CHR$(191);
FOR n = y1 + 1 TO y2 - 1
LOCATE n, x1
PRINT CHR$(179); SPACE$(x2 - x1 - 2); CHR$(179);
NEXT n
LOCATE y2, x1
PRINT CHR$(192); STRING$(x2 - x1 - 2, 196); CHR$(217);
DrawShadow x1, y1, x2, y2
END SUB
SUB DrawScreen
COLOR 15, 0
DrawBox 9, 6, 70, 19
COLOR 15, 0
LOCATE 7, 10
PRINT " C-SMENU1.1, By Calvin French, May (sometime) 1993 "
COLOR 14
LOCATE 8, 10
PRINT "-----------------------------------------------------------"
COLOR 13
LOCATE 9, 10
PRINT " This is a small and power packed versitile pulldown menu "
LOCATE 10, 10
PRINT " routine I put together awhile ago. I've added shadows in "
LOCATE 11, 10
PRINT " This version, 1.1. 1.0 diddn't have shadows. Okay if you "
LOCATE 12, 10
PRINT " want to use these routines, you may, but please put my "
LOCATE 13, 10
PRINT " name on it below your copyright or something. It's a very "
LOCATE 14, 10
PRINT " fast routine, and lacks only the ALT+Letter routine to "
LOCATE 15, 10
PRINT " access the menus, which is your job. It is all-QB, so you "
LOCATE 16, 10
PRINT " who don't like to use add-on libs should like this one! "
COLOR 14
LOCATE 17, 10
PRINT " Have fun, "
COLOR 15
LOCATE 18, 10
PRINT " - Calvin - "
END SUB
SUB DrawShadow (x1, y1, x2, y2)
DEF SEG = &HB800
YMem = y2 * 160
XMem = (x2 * 2) + 1
COLOR 8, 0
FOR n = x1 + 2 TO x2 + 1
MemLoc = YMem + n * 2 - 1
POKE MemLoc, 8
NEXT n
FOR n = y1 + 1 TO y2 + 1
MemLoc = ((n - 1) * 160) + XMem - 2
POKE MemLoc, 8
POKE MemLoc + 2, 8
NEXT n
DEF SEG
END SUB
SUB PullDownMenu (y1, TopX1(), BoxX1(), BoxX2(), LastOpt(),_
MenuDat$(), Fore, Back, SelectFore, SelectBack, hilightfore,_
hilightback, KeyFore, ReturnX, ReturnY)
DIM CurrY(UBOUND(MenuDat$, 1))
OldX = 1
CurrX = 1
PulledDown = false
DIM HotKey(UBOUND(MenuDat$, 1), UBOUND(MenuDat$, 2))
DIM leftside$(UBOUND(MenuDat$, 1), UBOUND(MenuDat$, 2))
DIM rightside$(UBOUND(MenuDat$, 1), UBOUND(MenuDat$, 2))
COLOR Fore, Back
LOCATE y1, 1
PRINT SPACE$(80);
GOSUB PrintTopBar
GOSUB SaveScreen2
FOR x = 1 TO UBOUND(MenuDat$, 1)
FOR y = 1 TO LastOpt(x)
HotKeyLoc = INSTR(MenuDat$(x, y), "^")
leftside$(x, y) = MID$(MenuDat$(x, y), 1, HotKeyLoc - 1)
HotKey(x, y) = ASC(MID$(MenuDat$(x, y), HotKeyLoc + 1))
rightside$(x, y) = MID$(MenuDat$(x, y), HotKeyLoc + 2)
NEXT y
NEXT x
IF ReturnX > 0 OR ReturnY > 0 THEN
FOR n = 1 TO UBOUND(MenuDat$, 1)
CurrY(n) = 1
NEXT n
CurrX = ReturnX
CurrY(CurrX) = ReturnY
PulledDown = true
GOSUB PrintCurrentMenu
END IF
GOSUB PrintCurrentTop
DO
GOSUB PrintMenu
DO
key$ = INKEY$
LOOP UNTIL LEN(key$)
KeyCode = ASC(RIGHT$(key$, 1))
SELECT CASE KeyCode
CASE 75 'left
CurrX = CurrX - 1
IF CurrX < 1 THEN CurrX = UBOUND(MenuDat$, 1)
CASE 77 'right
CurrX = CurrX + 1
IF CurrX > UBOUND(MenuDat$, 1) THEN CurrX = 1
CASE 72 'up
IF PulledDown = true THEN
CurrY(CurrX) = CurrY(CurrX) - 1
IF MenuDat$(CurrX, CurrY(CurrX)) = "^:" THEN CurrY(CurrX) = CurrY(CurrX) - 1
IF CurrY(CurrX) < 1 THEN CurrY(CurrX) = LastOpt(CurrX)
GOSUB PrintCurrentMenu
END IF
CASE 80 'down
IF PulledDown = false THEN
FOR n = 1 TO UBOUND(MenuDat$, 1)
CurrY(n) = 1
NEXT n
PulledDown = true
ELSE
CurrY(CurrX) = CurrY(CurrX) + 1
IF CurrY(CurrX) > LastOpt(CurrX) THEN CurrY(CurrX) = 1
IF MenuDat$(CurrX, CurrY(CurrX)) = "^:" THEN CurrY(CurrX) = CurrY(CurrX) + 1
IF CurrY(CurrX) > LastOpt(CurrX) THEN CurrY(CurrX) = 1
END IF
GOSUB PrintCurrentMenu
CASE 13
IF PulledDown = true THEN
GOSUB RestoreScreen2
ReturnX = CurrX
ReturnY = CurrY(CurrX)
EXIT SUB
ELSE
PulledDown = true
FOR n = 1 TO UBOUND(MenuDat$, 1)
CurrY(n) = 1
NEXT n
GOSUB PrintCurrentMenu
END IF
CASE ELSE
KeyCode = KeyCode OR 32
Search = HotKey(CurrX, CurrY(CurrX)) OR 32
found = false
FOR n = CurrY(CurrX) + 1 TO LastOpt(CurrX)
Search = HotKey(CurrX, n) OR 32
IF Search = KeyCode THEN
CurrY(CurrX) = n
found = true
EXIT FOR
END IF
NEXT
FOR n = 1 TO CurrY(CurrX)
Search = HotKey(CurrX, n) OR 32
IF Search = KeyCode THEN
IF found = false THEN
CurrY(CurrX) = n
found = true
END IF
EXIT FOR
END IF
NEXT n
IF found = true THEN
GOSUB PrintCurrentMenu
ReturnX = CurrX
ReturnY = CurrY(CurrX)
GOSUB RestoreScreen2
EXIT SUB
END IF
END SELECT
LOOP
EXIT SUB
PrintTopBar:
FOR n = 1 TO UBOUND(MenuDat$, 1)
LOCATE y1, TopX1(n)
PRINT MenuDat$(n, 0);
NEXT n
RETURN
PrintCurrentTop:
LOCATE y1, TopX1(CurrX)
COLOR SelectFore, SelectBack
PRINT MenuDat$(CurrX, 0);
RETURN
SaveScreen2:
PCOPY 0, 2 'PCOPY is very fast, and well suited for this task
RETURN
RestoreScreen2:
PCOPY 2, 0 'PCOPY is very fast, and well suited for this task
RETURN
PrintMenu:
IF CurrX <> OldX THEN
GOSUB RestoreScreen2
GOSUB PrintCurrentTop
IF PulledDown = true THEN
GOSUB PrintCurrentMenu
END IF
END IF
OldX = CurrX
RETURN
PrintCurrentMenu:
COLOR Fore, Back
LOCATE y1 + 1, BoxX1(CurrX)
PRINT CHR$(218); STRING$(BoxX2(CurrX) - BoxX1(CurrX) - 1, CHR$(196)); CHR$(191)
FOR n = 1 TO LastOpt(CurrX)
LOCATE y1 + n + 1, BoxX1(CurrX)
PRINT CHR$(179);
IF MenuDat$(CurrX, n) <> "^:" THEN
IF n <> CurrY(CurrX) THEN
COLOR Fore, Back
PRINT leftside$(CurrX, n);
COLOR KeyFore, Back
PRINT CHR$(HotKey(CurrX, n));
COLOR Fore, Back
PRINT rightside$(CurrX, n);
PRINT SPACE$(BoxX2(CurrX) - BoxX1(CurrX) - LEN(MenuDat$(CurrX, n)));
ELSE
COLOR hilightfore, hilightback
PRINT leftside$(CurrX, n);
COLOR KeyFore, hilightback
PRINT CHR$(HotKey(CurrX, n));
COLOR hilightfore, hilightback
PRINT rightside$(CurrX, n);
PRINT SPACE$(BoxX2(CurrX) - BoxX1(CurrX) - LEN(MenuDat$(CurrX, n)));
END IF
COLOR Fore, Back
PRINT CHR$(179);
ELSE
COLOR Fore, Back
LOCATE y1 + n + 1, BoxX1(CurrX)
PRINT CHR$(195); STRING$(BoxX2(CurrX) - BoxX1(CurrX) - 1, CHR$(196)); CHR$(180);
END IF
NEXT n
LOCATE y1 + LastOpt(CurrX) + 2, BoxX1(CurrX)
PRINT CHR$(192); STRING$(BoxX2(CurrX) - BoxX1(CurrX) - 1, CHR$(196)); CHR$(217)
DrawShadow BoxX1(CurrX), y1 + 1, BoxX2(CurrX) + 1, LastOpt(CurrX) + 3
RETURN
END SUB
'
' From: IAN REMMLER Sent: 07-17-93 21:57
' To: ALL Rcvd: -NO-
' Re: 3D STRANGE ATTRACTOR DEMO
'
'Hey everybody!
'Here's a 3D attractor demo that lets you rotate the attractor on
'the X, Y, and Z axes then view it. (like on FractInt) If you have
'a mouse you can use it to zoom in. I didn't spend time on putting
'in a keyboard equivalent, so if you don't have a mouse you can make
'a routine for the keyboard, or change the WINDOW statement manually.
'Have fun with it, and feel free to hack it to pieces.
DECLARE SUB Crsr (x!)
DECLARE SUB zoom ()
DECLARE FUNCTION MPos (Coord)
DECLARE FUNCTION MBttn ()
'$INCLUDE: 'qb.bi'
DIM SHARED Regs AS RegTypeX
DIM sine(359) AS SINGLE, cosine(359) AS SINGLE
CONST pi = 3.1415926535#
dist = 100 'This is the distance from the screen used in the
'perspective formula.
AngleX = 0 '\
AngleY = 0 ' > angles of rotation
AngleZ = 0 '/
SCREEN 9
PRINT "Creating SIN/COS Tables..."
PRINT : PRINT "Press F1 any time to goto Main Menu."
PRINT "Press F2 any time to exit program."
PRINT : PRINT "If you have a mouse, you can use the LMB to"
PRINT "zoom in on an attractor,or the RMB to exit."
LOCATE 24, 1: PRINT "Strange Attractor Demo - By: Ian Remmler.";
twirl$ = "\-/" + CHR$(179)
FOR t = 0 TO 359 'create sine/cosine tables
LOCATE 1, 28: PRINT MID$(twirl$, t MOD 4 + 1, 1)
sine(t) = SIN(t * (pi / 180))
cosine(t) = COS(t * (pi / 180))
NEXT
KEY(1) ON '\
KEY(2) ON ' \ sets up event trapping on F1 & F2
ON KEY(1) GOSUB Main ' /
ON KEY(2) GOSUB Leave'/
GOSUB WhichOne
Lorenz: 'Converted from the FractInt documentation.
WINDOW (-32, -0)-(32, 35)
dt = .05
a = 3
b = 15
c = 1
Lstart:
x = 1
y = 1
z = 1
CLS
GOSUB Translate
PSET (xxx, yyy)
Crsr 1
DO
xx = x: yy = y: zz = z
x = xx + (-a * xx * dt) + (a * yy * dt)
y = yy + (b * xx * dt) - (yy * dt) - (zz * xx * dt)
z = zz + (-c * zz * dt) + (xx * yy * dt)
GOSUB Translate
Crsr 0: LINE -(xxx, yyy): Crsr 1
IF MBttn = 1 THEN zoom: GOTO Lstart
LOOP UNTIL MBttn = 2
Crsr 0
GOSUB Leave
Rossler: 'Also from FractInt docs.
WINDOW (-64, -35)-(64, 35)
dt = .05
a = .2
b = .2
c = 5.7
Rstart:
x = 1
y = 1
z = 1
CLS
GOSUB Translate
PSET (xxx, yyy)
Crsr 1
DO
xx = x: yy = y: zz = z
x = xx - yy * dt - zz * dt
y = yy + xx * dt + a * yy * dt
z = zz + b * dt + xx * zz * dt - c * zz * dt
GOSUB Translate
Crsr 0: LINE -(xxx, yyy): Crsr 1
IF MBttn = 1 THEN zoom: GOTO Rstart
LOOP UNTIL MBttn = 2
Crsr 0
GOSUB Leave
Mutant: 'Wrote this one all by myself! Pretty spiffy, huh?
WINDOW (-32, -17.5)-(32, 17.5)
dt = .02
a = 8
b = 10
c = 10
Mstart:
x = 1
y = 1
z = 1
CLS
GOSUB Translate
PSET (xxx, yyy)
Crsr 1
DO
xx = x: yy = y: zz = z
x = xx - (a * zz * dt) + (-a * yy * dt)
y = yy + (b * xx * dt) - (yy * dt) - (zz * xx * dt)
z = zz + (-c * zz * dt) - (xx * yy * dt)
GOSUB Translate
Crsr 0: LINE -(xxx, yyy): Crsr 1
IF MBttn = 1 THEN zoom: GOTO Mstart
LOOP UNTIL MBttn = 2
Crsr 0
GOSUB Leave
Translate: 'converts x,y,z coords. to x,y so they can be put on
'the screen. We use the basic rotation formula on the
'X axis, then Y, then Z.
za = z * sine(AngleX) - y * cosine(AngleX) '\ X rotation
ya = z * cosine(AngleX) + y * sine(AngleX) '/
za = za * sine(AngleY) - x * cosine(AngleY) '\ Y rotation
xa = za * cosine(AngleY) + x * sine(AngleY) '/
xa = ya * sine(AngleZ) - xa * cosine(AngleZ) '\ Z rotation
ya = ya * cosine(AngleZ) + xa * sine(AngleZ) '/
xxx = xa * (dist / (dist + za))
yyy = ya * (dist / (dist + za))
RETURN
Main:
KEY(1) ON
ON KEY(1) GOSUB Main
CLS : PRINT "Main Menu!"
PRINT "1. Select an Attractor."
PRINT "2. Change Parameters."
PRINT "3. Change Angles of Rotation."
PRINT
DO: q = VAL(INKEY$)
LOOP WHILE q = 0
ON q GOSUB WhichOne, Params, Angles
CLS
RETURN
WhichOne:
CLS : PRINT "Select an Attractor!"
PRINT : PRINT "1. Lorenz"
PRINT "2. Rossler"
PRINT "3. Mutant"
PRINT
DO: q = VAL(INKEY$)
LOOP WHILE q = 0
ON q GOSUB Lorenz, Rossler, Mutant
Params:
PRINT "Input New Parameters!"
PRINT
PRINT "A= "; a; " "; : INPUT a
PRINT "B= "; b; " "; : INPUT b
PRINT "C= "; c; " "; : INPUT c
PRINT "DT= "; dt; " "; : INPUT dt
CLS
RETURN
Angles:
PRINT "Change Angles of Rotation!"
PRINT
PRINT "X= "; AngleX; " "; : INPUT AngleX
PRINT "Y= "; AngleY; " "; : INPUT AngleY
PRINT "Z= "; AngleZ; " "; : INPUT AngleZ
PRINT "Perspective Distance= "; dist; " "; : INPUT dist
CLS
RETURN
Leave:
END
SUB Crsr (x) 'this sub turns the pointer on and off (Crsr 0=off)
SELECT CASE x '(Crsr 1=on)
CASE 0
Regs.ax = 2
CASE 1
Regs.ax = 1
END SELECT
CALL INTERRUPTX(&H33, Regs, Regs)
END SUB
FUNCTION MBttn 'returns which buttons are currently down.
Regs.ax = 3
CALL INTERRUPTX(&H33, Regs, Regs)
MBttn = Regs.bx
END FUNCTION
FUNCTION MPos (Coord) 'returns the x,y coords. of the mouse
Regs.ax = 3
CALL INTERRUPTX(&H33, Regs, Regs)
SELECT CASE Coord
CASE 0
MPos = Regs.cx
CASE 1
MPos = Regs.dx
END SELECT
END FUNCTION
SUB zoom 'zooms in on the attractor using the mouse.
Crsr 0
PCOPY 0, 1
Crsr 1
x1 = PMAP(MPos(0), 2)
y1 = PMAP(MPos(1), 3)
DO
Crsr 0
PCOPY 1, 0
x2 = PMAP(MPos(0), 2)
y2 = PMAP(MPos(1), 3)
Crsr 1
LINE (x1, y1)-(x2, y2), , B
WHILE PMAP(MPos(0), 2) = x2 AND PMAP(MPos(1), 3) = y2: WEND
LOOP WHILE MBttn = 1
WINDOW (x1, y1)-(x2, y2)
Crsr 0
END SUB
'